home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / TPTUTR~1.ZIP / PASCAL20.TXT < prev    next >
Text File  |  1996-03-21  |  20KB  |  552 lines

  1.                         Turbo Pascal for DOS Tutorial
  2.                              by Glenn Grotzinger
  3.                        Part 20: Miscallaneous Topics
  4.                      copyright (c) by Glenn Grotzinger
  5.  
  6. Hello.  Here is a solution for the programming problem that was given in
  7. part 19 with a little background explanation.
  8.  
  9. PART19A.PAS
  10. -----------
  11. This was the program written to generate the binary data file for use with
  12. the actual word checker.  A text file was written to facilitate reading in
  13. the data, one word per line, to ease implementation.
  14.  
  15. program part19a;
  16.  
  17.   var
  18.     infile: text;
  19.     outfile: file;
  20.     datastring: string[14];
  21.   begin
  22.     assign(infile, 'SOMEWRDS.TXT');
  23.     assign(outfile, 'SOMEWRDS.DAT');
  24.     reset(infile);
  25.     rewrite(outfile, 1);
  26.  
  27.     readln(infile, datastring);
  28.     while not eof(infile) do
  29.       begin
  30.         blockwrite(outfile, datastring, sizeof(datastring));
  31.         writeln(datastring);
  32.         readln(infile, datastring);
  33.       end;
  34.     blockwrite(outfile, datastring, sizeof(datastring));
  35.     writeln(datastring);
  36.  
  37.     close(infile);
  38.     close(outfile);
  39.   end.
  40.  
  41. PART19B.PAS
  42. -----------
  43. This is the actual word checker.  It is made so the words typed into the
  44. keyboard do not have to be case sensitive with reference to the datafile.
  45. All prevalent errors have error-checking code.
  46.  
  47. program part19b;
  48.  
  49.   type
  50.     strtype = string[14];
  51.     nodeptr = ^node;
  52.     node = record
  53.       str: strtype;
  54.       left, right: nodeptr;
  55.     end;
  56.  
  57.   var
  58.     tree: nodeptr;
  59.     datastring: strtype;
  60.     infile: file;
  61.  
  62.   procedure opendatafile(var infile: file; afile: string);
  63.     begin
  64.       assign(infile, afile);
  65.       {$I-}reset(infile, 1);{$I+}
  66.       if IOResult <> 0 then
  67.         begin
  68.           writeln('Terminating.  ', afile, ' does not exist!');
  69.           halt(1);
  70.         end;
  71.     end;
  72.  
  73.   function upstr(instr: strtype):strtype;
  74.     var
  75.       i: byte;
  76.       tempstr: strtype;
  77.     begin
  78.       tempstr := '';
  79.       for i := 1 to length(instr) do
  80.         tempstr := tempstr + upcase(instr[i]);
  81.       upstr := tempstr;
  82.     end;
  83.  
  84.   procedure memoryerror;
  85.     begin
  86.       writeln('Out of memory.');
  87.       halt(1);
  88.     end;
  89.  
  90.   procedure deletetree(var tree: nodeptr);
  91.     begin
  92.       if tree <> nil then
  93.         begin
  94.           deletetree(tree^.right);
  95.           deletetree(tree^.left);
  96.           dispose(tree);
  97.         end;
  98.     end;
  99.  
  100.   procedure inserttree(datastring: strtype; var tree: nodeptr);
  101.     begin
  102.       if tree = nil then
  103.         begin
  104.           if memavail - sizeof(tree) > 0 then
  105.             new(tree)
  106.           else
  107.             memoryerror;
  108.           tree^.str := datastring;
  109.           tree^.left := nil;
  110.           tree^.right := nil;
  111.         end
  112.       else
  113.         begin
  114.           if upstr(datastring) > tree^.str then
  115.             inserttree(upstr(datastring), tree^.right);
  116.           if upstr(datastring) < tree^.str then
  117.             inserttree(upstr(datastring), tree^.left);
  118.         end;
  119.     end;
  120.  
  121.   procedure buildsearch(var infile: file; var tree: nodeptr);
  122.     var
  123.       datastring: strtype;
  124.     begin
  125.       blockread(infile, datastring, sizeof(datastring));
  126.       while not eof(infile) do
  127.         begin
  128.           inserttree(datastring, tree);
  129.           blockread(infile, datastring, sizeof(datastring));
  130.         end;
  131.       inserttree(datastring, tree);
  132.       close(infile);
  133.     end;
  134.  
  135.   procedure searchtree(datastring: strtype; tree: nodeptr);
  136.     begin
  137.       if tree = nil then
  138.         writeln(datastring, ' was not found in the data file.')
  139.       else
  140.         begin
  141.           if datastring = tree^.str then
  142.             writeln(datastring, ' was found in the data file.')
  143.           else
  144.             begin
  145.               if datastring > tree^.str then
  146.                 searchtree(datastring, tree^.right);
  147.               if datastring < tree^.str then
  148.                 searchtree(datastring, tree^.left);
  149.             end;
  150.         end;
  151.     end;
  152.  
  153.   procedure promptwrite;
  154.     begin
  155.       writeln('Type QUIT to terminate.');
  156.       writeln('Type a word, and we''ll see if it''s in the database.');
  157.       writeln;
  158.     end;
  159.  
  160.   begin
  161.     writeln('Database checker.');
  162.     writeln;
  163.     opendatafile(infile, 'SOMEWRDS.DAT');
  164.     buildsearch(infile, tree);
  165.     promptwrite;
  166.     readln(datastring);
  167.     while (upstr(datastring) <> 'QUIT') do
  168.       begin
  169.         searchtree(upstr(datastring), tree);
  170.         promptwrite;
  171.         readln(datastring);
  172.       end;
  173.     deletetree(tree);
  174.   end.
  175. The written explanation of the delete code that was written in the example,
  176. to completely remove the BST from memory...
  177.  
  178. We know that in a pointer-linked structure if we remove a node that has
  179. links assigned to it, that we will cause a heap leak and render the rest
  180. of the data structure unremovable from memory.  In a BST, that case becomes
  181. when both right and left branches of the node are nil.  We have to use
  182. recursion, evidently, since the problem of deleting the entire binary tree
  183. comes down to many smaller problems of deleting nodes with both sides
  184. assigned to nil, basically.  So basically, if we observe those lines of
  185. code, recursion occurs until both the left and right sides of nodes become
  186. nil, and then a dispose occurs.  In working back up in returning from the
  187. procedure calls, the tree is essentially disposed of from the bottom up to
  188. the root node, in reverse from the way the insert code illustration created
  189. it.
  190.  
  191. Contents for Part 20
  192. ====================
  193. Since this part contains many random varied topics, a table of contents is
  194. in order....
  195.  
  196.         A) Linking OBJ code into Pascal programs.
  197.         B) Including ASM or inline statements in Pascal programs.
  198.         C) Hooking an interrupt in pascal programs.
  199.         D) Calling an interrupt procedure.
  200.         E) Conditional Compiler Compilation.
  201.  
  202. Linking OBJ code into Pascal programs
  203. =====================================
  204. This is a short program, which describes the exact process, USING A C
  205. program's function (the topic of using C OBJ's came up).  If you do
  206. this with C OBJ, the code must not make any calls to any C libraries,
  207. or make any calls to a DLL.
  208.  
  209. A description of what's going on...
  210.  
  211. TEST1.H
  212.  
  213. int far pascal add2numbers(int num1, int num2)
  214.  
  215.   {
  216.     return (num1 + num2);
  217.   }
  218.  
  219. This is a C header file with a small defined function in it.  You MUST
  220. define it using either void far pascal for a procedure or <datatype> far
  221. pascal for a function.  I describe a function in test1.h.  For those who
  222. wonder, a header file in C functions much like a unit does in Pascal.
  223.  
  224. For an OBJ you intend to use, each and every function must be defined in
  225. the resultant pascal program.  (I know it is not liked, and you SHOULD
  226. NEVER normally post attachments in c.l.p.b.) With differences
  227. between so many different C compilers and syntaxes that exist in the world,
  228. I will post the OBJ file I got from the compiler I use, for purposes of
  229. enabling others to test this.  I recommend you to cut and paste it before
  230. you uu or xx encode it if you need to obtain it.
  231.  
  232. begin 644 test1.obj
  233. M@`D`!U1%4U0Q+D..B!\````;5$,X-B!";W)L86YD(%1U<F)O($,K*R`S+C`P
  234. MD8@/``#I=G%"(`=415-4,2Y#3H@&``#E`0``C(@5``#F!&YU;3($"@8`!&YU
  235. M;3$$"@@`2(@&``#E`08`AH@@``#F!F%N<W=E<@0"_O\$;G5M,00*"``$;G5M
  236. M,@0*!@"DB`4``.<:`'*(!0``YQH`<H@*``#N`0``!@`4`&6(`P``Z8R(!0``
  237. MZ@$+?98H```%7U1%6%0$0T]$105?1$%4001$051!!%]"4U,#0E-3!D1'4D]5
  238. M4-&8!P`H&@`"`P$9F`<`2```!`4!#Y@'`$@```8'`0N:!@`(_P+_`U60$@``
  239. M`0M!1$0R3E5-0D524P```#N("P``XQ@````C!`4`1H@%``#A&!ABH!X``0``
  240. M58OL@^P"BT8(`T8&B4;^BT;^ZP"+Y5W*!`"ZB!```.@`!U1%4U0Q+D-V<4(@
  241. <3I03```!`@````8`!@`'``\`"``4`!B*`@``=```
  242. `
  243. end
  244.  
  245. TEST2C.PAS
  246.  
  247. {$F+}
  248. program test2c;
  249.  
  250.   {$L TEST1.OBJ}
  251.   function add2numbers(num1, num2: integer): integer;external;
  252.   
  253.   begin
  254.     writeln(add2numbers(2, 3));
  255.   end.
  256.  
  257.  
  258. This is the pascal code using the OBJ.  Basically, the $F+ compiler
  259. directive is required.  Then observe the usage of the $L compiler
  260. directive.  The $L specifies the OBJ file name to link in.
  261. Also note the use of the external; statement at the end of the function
  262. declaration.  That specifies that the function is defined externally,
  263. and not in any Pascal unit or defined in the program itself.  The
  264. function or procedure declaration listed in the pascal program must
  265. match EXACTLY with the one defined in the OBJ file.  It does in this
  266. case.
  267.  
  268. Including ASM or inline statements in Pascal
  269. ============================================
  270. Pascal is a compiler, which translates the data into ASM, and then into
  271. machine language.  Naturally, if it takes the data to ASM, it can use
  272. straight assembler code as as well.  Take a look at the short procedure
  273. mockup below.
  274.  
  275. procedure dothis; assembler;
  276.   asm
  277.    { assembler code goes in here }
  278.   end;
  279.  
  280. asm is a keyword that may be used anywhere in code, which
  281. tells the compiler to start looking for assembler code instead of pascal code.
  282. The assembler keyword is one that can optionally be added to make the
  283. compiler look for an asm keyword instead of the begin at a beginning of a
  284. procedure or function.
  285.  
  286. An inline statement works much like a function call.  It makes it so
  287. that machine language can be used in pascal code.  For example (the hex
  288. codes are garbage I'm making up so don't use this in a real program),
  289. an inline statement can look like this...
  290.  
  291. inline($80/$23/$14/$36);
  292.  
  293. The codes can go on infinitely, and are separated by /'s...
  294.  
  295. References
  296. ==========
  297. There is so much specific information out there that is required to know
  298. about computers that references are almost always needed.  Here are a few
  299. of my recommendations.
  300.  
  301.    Ralf Brown's Interrupt List (INTERXXY.ZIP  XX being a version #, y
  302.        being a through e. -- this is a big set of text files!)
  303.        This is a listing of all known interrupt functions of a PC.
  304.  
  305.    The PC Game Programmer's Encyclopedia (PCGPE.ZIP)  This is a
  306.        collection of text files...some hold tutorials about how to
  307.        program in assembler and VGA, a lot of them hold specific
  308.        information about how to program peripherals of your system.
  309.        Another big download
  310.  
  311.    The SourceWare Archival Group collection (SWAG).  Very much known,
  312.        this is a collection of pascal code which has been donated by
  313.        programmers from all over the world that reference how to do
  314.        various topics.  I recommend you use this code only as a reference
  315.        for instruction and do not copy the code.  This is almost a 7.5MB
  316.        download.
  317.  
  318. Hooking an Interrupt
  319. ====================
  320. An interrupt is a signal made from the computer to the CPU for many
  321. functions that occur in the computer.  There are many different types
  322. and numbers of interrupts.  There are two basic types, though, hardware
  323. and software interrupts.
  324.  
  325. A interrupt is put in simpler terms is an "attention order" from some
  326. component of the system.  In most common systems, there are 255 of these
  327. available, both software and hardware.
  328.  
  329. Below is an example where we hook an interrupt.  Hooking an interrupt
  330. involves the replacement and augmentation of the basic system interrupt
  331. process with something else, or nothing at all (if we want to temporarily
  332. disable a piece of hardware for a security program, for example).
  333.  
  334. What this program does is hook the interrupt that handles the internal
  335. PC timer.  That function is augmented by a count that is added by the
  336. procedure "count" listed below.  As a background, for the PC timer,
  337. approximately 18.2 timer ticks occur every second, or 0.054945 seconds
  338. per tick.  Ultimately, you can see by running this program, that all it
  339. does is wait for you to press a key. 
  340.  
  341. program writehello; uses dos;
  342.  
  343.   var
  344.     intsave: procedure;
  345.     counter: integer;
  346.  
  347.  {$F+}
  348.  procedure count; interrupt;
  349.    begin
  350.      inc(counter);
  351.      inline($9C); { prods an interrupt }
  352.      intsave;
  353.    end;
  354.  {$F-}
  355.  
  356.  begin
  357.    writeln('Press a key.');
  358.    getintvec($1C, @intsave); { get current int. procedure. }
  359.    setintvec($1C, @count);   { replace it with our procedure. }
  360.    readln;
  361.    setintvec($1C, @intsave); { save the regular int. procedure back }
  362.    writeln(counter, ' timer ticks have passed.');
  363.  end.
  364.  
  365. We will now describe what is different here....
  366.  
  367. getintvec() is a TP DOS unit function which pulls the procedure currently
  368.   defined on that interrupt.
  369. setintvec() is a TP DOS unit function that resets the function defined by
  370.   the interrupt.
  371. The interrupt keyword must be placed on the procedure definition, and the
  372.   procedure definition must be defined as far, like you see.
  373.  
  374. A word of note for a procedure defined as an interrupt...the original
  375. interrupt procedure must be called within that interrupt procedure.  The
  376. inline($9C) seems to also be a required element.
  377.  
  378. NOTE: Hooking an interrupt is essentially a dangerous proposition, to a
  379. degree.  Be sure that you are careful and learn what happens on each
  380. interrupt and know what the interrupt does before you try to hook it.
  381. Doing this actually CHANGES the hardware behavior of your system.
  382. If attempting to hook an interrupt causes the system to behave adversely,
  383. STOP the program right there (reboot the system even), and do not attempt
  384. the code again until you are aware of what exactly happened and what is the
  385. right thing to do to accomplish what you want.  For example, I once tried
  386. hooking a replacement procedure on the interrupt for the hard disk, and I
  387. had garbled output from the directory commands until I rebooted....
  388.  
  389. Basically, BE VERY CAREFUL IN HOOKING INTERRUPTS.
  390.  
  391. Calling Software Interrupt Procedures
  392. =====================================
  393. The system (hardware and operating system) has varied software interrupts
  394. which may be called to perform certain functions in the system.  This is
  395. how most of the file access works in the other functions of the DOS unit,
  396. such as assign, reset, rewrite, and close; for example.
  397.  
  398. The record registers or tregisters are defined in the DOS unit or WinDOS
  399. unit respectively. It looks like:
  400.  
  401. type
  402.   registers = record
  403.     case integer of
  404.       0: (AX, BX, CX, DX, BP, SI, DI, DS, ES, Flags: Word);
  405.       1: (AL, AH, BL, BH, CL, CH, DL, DH: byte);
  406.     end;
  407.  
  408. This is considered a variant record, which may be used to discriminate
  409. the latter part of the field if data dictates it.  If you are familiar
  410. with the assembler language, you should recognize the fields from the
  411. record.
  412.  
  413. Here is a sample program, which holds an example of a software interrupt
  414. call.  In this case, the program is calling function 9 (?) of interrupt
  415. $21, which involves writing a string out via regular BIOS calls (this is
  416. what TP does when it writes out a string).  Note the conversion of the
  417. pascal string to a blocked array string.  A string must end with a "$"
  418. when using this function.
  419.  
  420. program writetest; uses dos;
  421.  
  422.   type
  423.     strtype = array[0..255] of char;
  424.  
  425.   procedure writestring(astring: string);
  426.     var
  427.       tstr: strtype;
  428.       i: byte;
  429.       regs: registers;
  430.     begin
  431.       i := 1;
  432.       repeat
  433.         tstr[i] := astring[i];
  434.         inc(i);
  435.       until i = length(astring);
  436.       astring[i+1] := '$'; 
  437.  
  438.       Regs.Ah := 9;
  439.       Regs.Ds := Seg(astring);
  440.       Regs.Dx := Ofs(astring);
  441.       intr($21, regs);  {call interrupt $21 using regs register set }
  442.     end;
  443.  
  444.   begin
  445.     writestring('Hello all!');
  446.   end.
  447.  
  448. Amazingly, this program is 304 bytes smaller as an EXE than the
  449. equivalent program  listed below written using exclusively the TP write
  450. procedure...remember, the most efficient code is USUALLY the code with
  451. the shortest # of lines, but not ALWAYS.
  452.  
  453. program writetest1;
  454.   begin
  455.     write('Hello all!');
  456.   end.
  457.  
  458. This program also introduces a few new function calls.  Intr() is a
  459. Pascal function in the DOS unit which executes a function call.  It is
  460. called like the example program.
  461.  
  462. Memory addresses, as you may have already seen from following pointers
  463. using the debugger, are set up in a 20-bit segment/offset manner (20-bit
  464. memory = 1024KB addressable -- they did this for the original 8088's...).
  465. Seg() shows the memory segment of any variable you place in there.
  466. Ofs() shows the memory offset of any variable you place in there.
  467. Addr() shows the complete memory address of any variable you place in
  468. there.
  469.  
  470. Consequently, we may also use the function MsDos() in this case to replace
  471. the Intr() call above.  MsDos() is an Intr() call to interrupt $21 (the
  472. DOS operating system control interrupt).
  473.  
  474. Conditional Compiler Compilation
  475. ================================
  476. Let us first define a few more compiler directives for your collection of
  477. knowledge....defaults marked with ~
  478.  
  479.                 + state                      - state
  480.                 --------                    ----------
  481. $N               FPU on                     ~FPU off
  482. $G               286 code gen. on           ~286 code gen. off
  483. $E              ~FPU emulation on            FPU emulation off
  484.  
  485. These are compiler directives often used conditionally.  Conditional
  486. compilation is essentially another language, independent of the actual
  487. language code.  Identifiers may not be used from code in compiler
  488. directives and vice versa.  Let's look at a few of the pertinent
  489. constants that Pascal defines in relation to this....
  490.  
  491. MSDOS => boolean: indicates that the operating system is MS-DOS or PC-DOS
  492. CPU87 => boolean: true if there is a math coprocessor (FPU) present.
  493.  
  494. The only construct you will typically see is an if then or if then else.
  495.  
  496. The compiler directives I listed above are most commonly used with this
  497. type of situation.  There are additional floating point data types available
  498. called comp, single, double, and extended; which are usable via the FPU
  499. generally.  The definitions of these variables may be defined using the
  500. conditional compiler directives....
  501.  
  502. The conditional compiler directives we need to know are:
  503.  
  504. {$IFDEF <name>}                                If item defined
  505. {$IFNDEF <name> }                              if item not defined 
  506. {$ELSE}                                        ELSE directive
  507. {$IFOPT <compiler directive>}                  If compiler opt..
  508. {$ENDIF}                                       END IF STATEMENT
  509. {$DEFINE <name>}                               define a statement
  510. {$UNDEF <name>}                                undefine a statement.
  511.  
  512. Functionally, these work and compile the source code if certain things are
  513. true.  For example, {$IFOPT N+} <some code> {$ENDIF} in some code will make
  514. it so the code represented by <some code> will only be used if N+ is defined.
  515.  
  516. Here is a short example program using conditional compiler directives....
  517.  
  518. program condcomptest;
  519.  
  520.   begin
  521.     {$IFDEF CPU87}
  522.       writeln('There is a math coprocessor in the system.');
  523.     {$ELSE}
  524.       writeln('There is not a math coprocessor in the system.');
  525.     {$ENDIF}
  526.   end.
  527. You will notice that only ONE writeln statement will execute itself.  The
  528. dependence will be whether a FPU exists in your system.
  529.  
  530. Practice Programming Problem #20
  531. ================================
  532. Write a program which will count the number of keystrokes a user presses
  533. in a period of 15 seconds.  Use proper programming interest in this
  534. program, and be sure to be friendly to the user with this program.
  535.  
  536. Background information.
  537.    Keyboard interrupt is $09.  Interrupt occurs 2 times for every
  538.    key pressed.
  539.  
  540. Notes: Unfortunately, in all attempts I've made, to make it TERMINATE
  541. exactly on 15 seconds, it would cause a nasty problem.  The program will
  542. have to read in at least one key after termination.  Do not worry about
  543. this.
  544.  
  545. Next Time
  546. =========
  547. We use the BGI.  Write comments to ggrotz@2sprint.net.
  548.  
  549.  
  550.  
  551.  
  552.